#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <ctype.h>
#include <errno.h>
#include "str.h"
#include "err.h"

int strtoulong(const char *s, unsigned long *ul)
{
	char *end;

	errno = 0;
	*ul = strtoul(s, &end, 0);
	if (errno) {
		perrmsg(s);
		return -1;
	}
	if (end == s) {
		pmsgmsg("No digits were found", s);
		return -1;
	}
	if (end[0] != '\0') {
		pmsgmsg("Trailing garbage", s);
		return -1;
	}

	return 0;
}

/**
 * strtouint() - convert time notation
 * @return: -1 if ill-formated or out of range
 * syntax: [...][hh:[mm:]]ss[.xxx]
 */
int strtotime(const char *s, long *f)
{
	unsigned long p[4], msec_digit;
	unsigned long hour, min, sec, msec;
	int sign;
	int colon, dot;

	if (s[0] == '\0') {
		pmsgmsg("No digits were found", s);
		return -1;
	}

	sign = 1;
	if (*s == '+') {
		s++;
	} else if (*s == '-') {
		sign = -1;
		s++;
	}

	colon = 0;
	dot = 0;
	memset(p, 0, sizeof(p));
	msec_digit = 0;

	for (; *s; s++) {
		if (isdigit(s[0])) {
			if (p[colon + dot] > (ULONG_MAX - 
					(unsigned long)(*s - '0')) / 10U)
				goto erange;
			p[colon + dot] = p[colon + dot] * 10U +
						(unsigned long)(*s - '0');
			if (dot)
				msec_digit++;
		} else if (s[0] == ':') {
			colon++;
			if (colon >= 3 || dot)
				goto eilseq;

			if (!isdigit(s[1]) || !isdigit(s[2]) || isdigit(s[3]))
				goto eilseq;
		} else if (s[0] == '.') {
			if (dot)
				goto eilseq;
			dot = 1;
		} else {
			goto eilseq;
		}
	}

	/* mm...m:(ss), hh...h:(mm):ss */
	if (colon >= 1 && p[1] >= 60)
		goto erange;
	/* hh...h:mm:(ss) */
	if (colon >= 2 && p[2] >= 60)
		goto erange;

	hour = 0;
	min = 0;
	sec = 0;
	msec = 0;

	if (dot)
		msec = p[colon + dot];
	while (msec_digit > 3) {
		msec /= 10;
		msec_digit--;
	}
	while (msec_digit < 3) {
		msec *= 10;
		msec_digit++;
	}
	sec = p[colon];
	/* [hhh:]mmm:ss */
	if (colon >= 1)
		min = p[colon - 1];
	/* hhh:mm:ss */
	if (colon >= 2)
		hour = p[colon - 2];

	if (hour > (ULONG_MAX - min) / 60)
		goto erange;
	min += hour * 60;

	if (min > (ULONG_MAX - sec) / 60)
		goto erange;
	sec += min * 60;

	if (sec > (ULONG_MAX - msec) / 1000)
		goto erange;
	msec += sec * 1000;

	if ((sign > 0 && msec > (unsigned long)LONG_MAX) ||
	(sign < 0 && msec > -(unsigned long)LONG_MIN))
		goto erange;

	*f = sign * (long)msec;

	return 0;

eilseq:
	pmsgmsg("Trailing garbage", s);
	return -1;
erange:
	pmsgmsg(strerror(ERANGE), s);
	return -1;
}

/* trim white spaces */
char *trim(char *s)
{
	char *end;

	while (isspace(*s))
		s++;

	if (*s == 0)
		return s;

	for (end = s; *end; end++);
	end--;

	while (end > s && isspace(*end))
		end--;

	end[1] = 0;

	return s;
}

/* split and trim a string into two by comma */
void split(char *in, char **a, char **b)
{
	char *comma;
	comma = strchr(in, ',');
	if (!comma) {
		in = trim(in);
		*a = in;
		*b = in;
	} else {
		*a = in;
		*b = comma + 1;
		*comma = 0;
		*a = trim(*a);
		*b = trim(*b);
	}
}


